from app.Dao.HealthDao.FoodDao.FoodRecordDao import FoodRecordDAO  # 导入DAO层
from app.Models.HealthModels.FoodModels.FoodCateModel import FoodCate
from app.Models.HealthModels.FoodModels.FoodCalModel import FoodCal
from app.Models.AdminModels.UserModels.UserModel import User
from datetime import date, datetime
from typing import List, Dict, Optional, Union


class FoodRecordServices:
    """饮食摄入记录Service层：封装业务逻辑、参数校验、异常处理"""

    # -------------------------- 新增记录 --------------------------
    @staticmethod
    def add_food_record(
            food_cate_id: int,
            user_id: int,
            food_id: int,
            act_intake_gram: float,
            intake_date: Union[str, date],
            note: Optional[str] = None
    ):
        """
        新增饮食摄入记录（自动计算实际摄入热量，无需手动传入）
        :param food_cate_id: 食物类型ID
        :param user_id: 用户ID
        :param food_id: 食物ID（关联food_cal表）
        :param act_intake_gram: 实际克重（必须>0）
        :param intake_date: 摄入日期（字符串格式"YYYY-MM-DD"或date对象）
        :param note: 备注（可选）
        :return: 新增记录的完整信息（含关联名称）
        :returns False,: 业务异常（参数无效、关联数据不存在等）
        """
        # 1. 参数校验
        if not all([food_cate_id, user_id, food_id, act_intake_gram]):
            return False, "食物类型ID、用户ID、食物ID、实际克重为必填项"

        if act_intake_gram <= 0:
            return False, "实际克重必须大于0"

        # 校验日期格式（若为字符串）
        if isinstance(intake_date, str):
            try:
                intake_date = datetime.strptime(intake_date, '%Y-%m-%d').date()
            except ValueError:
                return False, ("日期格式错误，需为YYYY-MM-DD（如2025-11-03）")

        # 2. 校验关联数据是否存在（避免无效ID）
        if not FoodCate.query.get(food_cate_id):
            return False, f"食物类型ID={food_cate_id} 不存在"

        if not User.query.get(user_id):
            return False, f"用户ID={user_id} 不存在"

        food_cal = FoodCal.query.get(food_id)
        if not food_cal:
            return False, f"食物ID={food_id} 不存在"

        # 3. 业务逻辑：计算摄入的热量
        # 3.1 每克的热量
        kcal_per_gram = food_cal.kcal_per_piece / food_cal.gram_per_piece
        act_intake_kcal = round(kcal_per_gram * act_intake_gram, 2)  # 保留2位小数

        # 4. 调用DAO层新增记录
        try:
            record = FoodRecordDAO.add_food_record(
                food_cate_id=food_cate_id,
                user_id=user_id,
                food_id=food_id,
                act_intake_gram=act_intake_gram,
                act_intake_kcal=act_intake_kcal,
                intake_date=intake_date,
                note=note
            )
            return True, f"新增成功"
        except Exception as e:
            return False, f"新增饮食记录失败：{str(e)}"

    # -------------------------- 删除记录 --------------------------
    @staticmethod
    def delete_food_record(record_id: int, operator_user_id: int) -> bool:
        """
        删除饮食记录（添加权限校验：只能删除自己的记录）
        :param record_id: 记录ID
        :param operator_user_id: 操作人ID（当前登录用户ID）
        :return: True（删除成功）
        :returns False,: 业务异常（记录不存在、无删除权限）
        """
        # 1. 校验记录是否存在
        record = FoodRecordDAO.get_food_record_by_id(record_id)
        if not record:
            return False, (f"饮食记录ID={record_id} 不存在")

        # 2. 权限校验：只能删除自己的记录（管理员可跳过，需扩展可添加角色判断）
        if record.user_id != operator_user_id:
            return False, ("无权限删除他人的饮食记录")

        # 3. 调用DAO层删除
        try:
            delete_success = FoodRecordDAO.delete_food_record(record_id)
            if not delete_success:
                return False, ("删除记录失败")
            return True
        except Exception as e:
            return False, (f"删除饮食记录失败：{str(e)}")

    # -------------------------- 更新记录 --------------------------
    @staticmethod
    def update_food_record(
            record_id: int,
            operator_user_id: int,
            **kwargs
    ) -> Dict:
        """
        更新饮食记录（支持部分字段更新，自动重新计算热量）
        :param record_id: 记录ID
        :param operator_user_id: 操作人ID（当前登录用户ID）
        :param kwargs: 待更新字段（可选：food_cate_id、food_id、act_intake_gram、intake_date、note）
        :return: 更新后的完整记录信息（含关联名称）
        :returns False,: 业务异常（记录不存在、无权限、参数无效）
        """
        # 1. 校验记录是否存在
        record = FoodRecordDAO.get_food_record_by_id(record_id)
        if not record:
            return False, (f"饮食记录ID={record_id} 不存在")

        # 2. 权限校验：只能更新自己的记录
        if record.user_id != operator_user_id:
            return False, ("无权限更新他人的饮食记录")

        # 3. 处理更新字段的参数校验
        update_fields = {}

        # 校验食物类型ID（若传入）
        if 'food_cate_id' in kwargs and kwargs['food_cate_id'] is not None:
            food_cate_id = kwargs['food_cate_id']
            if not FoodCate.query.get(food_cate_id):
                return False, (f"食物类型ID={food_cate_id} 不存在")
            update_fields['food_cate_id'] = food_cate_id

        # 校验食物ID（若传入）
        if 'food_id' in kwargs and kwargs['food_id'] is not None:
            food_id = kwargs['food_id']
            food_cal = FoodCal.query.get(food_id)
            if not food_cal:
                return False, (f"食物ID={food_id} 不存在")
            update_fields['food_id'] = food_id
            # 标记需要重新计算热量
            need_recalc_kcal = True
        else:
            need_recalc_kcal = False

        # 校验实际克重（若传入）
        if 'act_intake_gram' in kwargs and kwargs['act_intake_gram'] is not None:
            act_intake_gram = kwargs['act_intake_gram']
            if act_intake_gram <= 0:
                return False, ("实际克重必须大于0")
            update_fields['act_intake_gram'] = act_intake_gram
            need_recalc_kcal = True

        # 校验摄入日期（若传入）
        if 'intake_date' in kwargs and kwargs['intake_date'] is not None:
            intake_date = kwargs['intake_date']
            if isinstance(intake_date, str):
                try:
                    intake_date = datetime.strptime(intake_date, '%Y-%m-%d').date()
                except ValueError:
                    return False, ("日期格式错误，需为YYYY-MM-DD")
            update_fields['intake_date'] = intake_date

        # 备注（直接传入，允许为空）
        if 'note' in kwargs:
            update_fields['note'] = kwargs['note']

        # 4. 重新计算热量（若食物ID或克重变更）
        if need_recalc_kcal:
            # 获取最新的食物信息（优先用更新后的food_id，否则用原record的food_id）
            food_id = update_fields.get('food_id', record.food_id)
            food_cal = FoodCal.query.get(food_id)
            act_intake_gram = update_fields.get('act_intake_gram', record.act_intake_gram)
            update_fields['act_intake_kcal'] = round(food_cal.cal_per_100g * act_intake_gram / 100, 2)

        # 5. 调用DAO层更新
        try:
            updated_record = FoodRecordDAO.update_food_record(record_id, **update_fields)
            if not updated_record:
                return False, ("更新记录失败")
        except Exception as e:
            return False, (f"更新饮食记录失败：{str(e)}")

        # 6. 返回带关联名称的完整信息
        return FoodRecordDAO.get_food_record_with_names(updated_record.id)

    # -------------------------- 查询单条记录（带名称） --------------------------
    @staticmethod
    def get_food_record_detail(record_id: int, operator_user_id: int) -> Dict:
        """
        查询单条饮食记录详情（带关联名称，含权限校验）
        :param record_id: 记录ID
        :param operator_user_id: 操作人ID（当前登录用户ID）
        :return: 记录详情字典
        :returns False,: 业务异常（记录不存在、无查看权限）
        """
        # 1. 调用DAO层查询带名称的记录
        record_detail = FoodRecordDAO.get_food_record_with_names(record_id)
        if not record_detail:
            return False, (f"饮食记录ID={record_id} 不存在")

        # 2. 权限校验：只能查看自己的记录（管理员可跳过）
        if record_detail['user_id'] != operator_user_id:
            return False, ("无权限查看他人的饮食记录")

        return record_detail

    # -------------------------- 按用户查询记录（带名称） --------------------------
    @staticmethod
    def get_user_food_records(
            user_id: int,
            operator_user_id: int,
            start_date: Optional[Union[str, date]] = None,
            end_date: Optional[Union[str, date]] = None
    ):
        """
        查询指定用户的饮食记录（带关联名称，含权限校验）
        :param user_id: 被查询的用户ID
        :param operator_user_id: 操作人ID（当前登录用户ID）
        :param start_date: 开始日期（可选）
        :param end_date: 结束日期（可选）
        :return: 记录列表（每条记录含关联名称）
        :returns False,: 业务异常（无查看权限、日期格式错误）
        """
        # 1. 权限校验：只能查询自己的记录（管理员可跳过）
        if user_id != operator_user_id:
            return False, "无权限查询他人的饮食记录"

        # 2. 处理日期参数格式
        try:
            if start_date and isinstance(start_date, str):
                start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
            if end_date and isinstance(end_date, str):
                end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
        except ValueError:
            return False, "日期格式错误，需为YYYY-MM-DD"

        # 3. 调用DAO层查询
        try:
            records = FoodRecordDAO.get_food_records_by_user_with_names(
                user_id=user_id,
                start_date=start_date,
                end_date=end_date
            )
            return True, records
        except Exception as e:
            return False, f"查询饮食记录失败：{str(e)}"

    # -------------------------- 管理员查询所有记录（分页+带名称） --------------------------
    @staticmethod
    def get_all_food_records(
            page: int = 1,
            per_page: int = 20,
            user_id: Optional[int] = None,
            start_date: Optional[Union[str, date]] = None,
            end_date: Optional[Union[str, date]] = None
    ):
        """
        管理员分页查询所有饮食记录（支持筛选用户、日期范围）
        :param page: 页码（默认1）
        :param per_page: 每页条数（默认20）
        :param user_id: 筛选用户ID（可选）
        :param start_date: 筛选开始日期（可选）
        :param end_date: 筛选结束日期（可选）
        :return: 分页结果（含总条数、总页数、当前页记录）
        :returns False,: 业务异常（日期格式错误）
        """
        # 1. 处理日期参数格式
        try:
            if start_date and isinstance(start_date, str):
                start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
            if end_date and isinstance(end_date, str):
                end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
        except ValueError:
            return False, "日期格式错误，需为YYYY-MM-DD"

        # 2. 调用DAO层分页查询（先获取基础分页对象）
        pagination = FoodRecordDAO.get_all_food_records(page=page, per_page=per_page)

        # 3. 补充筛选条件（用户ID、日期范围）
        # 注意：DAO层get_all_food_records未支持筛选，这里扩展逻辑（或修改DAO层，推荐修改DAO层添加筛选）
        # 临时方案：先获取当前页记录，再过滤（适合小数据量；大数据量需修改DAO层查询逻辑）
        filtered_records = []
        for record in pagination.items:
            # 筛选用户ID
            if user_id and record.user_id != user_id:
                continue
            # 筛选日期范围
            if start_date and record.intake_date < start_date:
                continue
            if end_date and record.intake_date > end_date:
                continue
            # 转换为带名称的字典
            print(type(record.intake_date))
            record_detail = FoodRecordDAO.get_food_record_with_names(record.id)
            filtered_records.append(record_detail)
        if len(filtered_records) == 0:
            return True, {
                'total': len(filtered_records),  # 筛选后的总条数（若需未筛选总条数，用pagination.total）
                'pages': (len(filtered_records) + per_page - 1) // per_page,  # 筛选后的总页数
                'page': page,
                'per_page': per_page,
                'items': filtered_records
            }
        # 4. 构造分页结果
        return True, {
            'total': len(filtered_records),  # 筛选后的总条数（若需未筛选总条数，用pagination.total）
            'pages': (len(filtered_records) + per_page - 1) // per_page,  # 筛选后的总页数
            'page': page,
            'per_page': per_page,
            'items': filtered_records
        }
